#include "Unit_Test.h"
// all trax programs must include trax.hpp
#include "trax.hpp"
#include "Image.h"
#include "Vector3.h"
#include "Sphere.h"
#include "HitRecord.h"
#include "PinHoleCamera.h"
#include "Scene.h"
#include "Shader.h"
#include "AABBox.h"
#include "atTriangle.h"
#include "bvhStack.h"
#include "atUtil.h"
#include "PhotonMap.h"
#include "MemoryManager.h"

namespace AtomTrace{
    
	void Unit_test_Vector3()
	{
		Vector3 vec1(1.0f, 2.0f, 1.0f);
		Vector3 vec2(2.0f, 3.0f, 0.0f);

		Vector3 vec3 = vec1 + vec2;

		printf("vec3_1[%f, %f, %f]\n", vec3[0], vec3[1], vec3[2]);

		//vec3 = Vector3::UNIT_X;
		//for(int i=0; i<1; i++)
		//printf("UNIT_X:[%f, %f, %f]\n", Vector3::UNIT_X[0], Vector3::UNIT_X[1], Vector3::UNIT_X[2]);
	}

	void Unit_test_BoxIntersection()
	{
		//using namespace AtomTrace;
		AABBox box(Vector3(-1.0f, -1.0f, -1.0f), Vector3(1.0f, 1.0f, 1.0f));

		int xres = GetXRes();
		int yres = GetYRes();
		int start_fb = GetFrameBuffer();
		Image image(xres, yres, start_fb);

		PinHoleCamera cam;

		cam.position.Set(4.0f, -15.0f, -2.0f);
		cam.dir.Set(-0.255551f, 0.958315, 0.127775);
		//cam.lookat = cam.position + gaze;
		cam.up.Set(0.0f, 0.0f, 1.0f);

		cam.fovy = 0.194f; // tan(22 degree)
		cam.imgWidth = xres;
		cam.imgHeight = yres;

		cam.UpdateCamParameter();

		int imageSize = xres * yres;
		Color red, black, res;
		red.Set(1.0f, 0.0f, 0.0f);
		black.Set(0.0f, 0.0f, 0.0f);

		Vector3 des;

		for(int pix = 0; pix < imageSize; pix++)
		{
			Ray ray = cam.CastRay(pix);
			HitRecord hit;
			hit.Init();
			bool isHit = false;
			//for(int objI = 0; objI < scene.numSpheres; objI++)
			//int objI = 2;
			{
				isHit |= box.Intersect(ray, hit.t);
			}

			if(isHit)
			{
				des = ray._o + ray._dir * hit.t;
				for(int i=0; i<3; i++)
				{
					des[i] = des[i] * 0.5f + 0.5f;
				}
				res.Set(des[0], des[1], des[2]);
				res.Clamp();
				image.set(pix, res);
				//printf("hit.normal[%f, %f, %f]\n", hit.N[0], hit.N[1], hit.N[2]);
				//printf("hit.P[%f, %f, %f]\n", hit.P[0], hit.P[1], hit.N[2]);
				//printf("[%i, %i] isHit\n");
			}else
			{
				image.set(pix, black);
			}

		}
	}

	void Unit_test_TriangleIntersection()
	{
		atTriangle tri1(Vector3(-1.0f, -0.5f, 0.0f),
						Vector3( 1.0f, -0.5f, 0.0f),
						Vector3( 0.0f,  1.0f, 0.0f));

		int xres = GetXRes();
		int yres = GetYRes();
		int start_fb = GetFrameBuffer();
		Image image(xres, yres, start_fb);

		PinHoleCamera cam;

		cam.position.Set(0.0f, 0.0f, 10.0f);
		cam.dir.Set(0.0f, 0.0f, -1.0f);
		//cam.lookat = cam.position + gaze;
		cam.up.Set(0.0f, 1.0f, 0.0f);

		cam.fovy = 0.194f; // tan(22 degree)
		cam.imgWidth = xres;
		cam.imgHeight = yres;

		cam.UpdateCamParameter();

		int imageSize = xres * yres;
		Color red, black, res;
		red.Set(1.0f, 0.0f, 0.0f);
		black.Set(0.0f, 0.0f, 0.0f);
		res = black;
		PointLight light;
		light.pos.Set(1.0f, 0.0f, 1.0f);

		Vector3 des;

		for(int pix = 0; pix < imageSize; pix++)
		{
			Ray ray = cam.CastRay(pix);
			HitRecord hit;
			hit.Init();
			bool isHit = false;
			//for(int objI = 0; objI < scene.numSpheres; objI++)
			//int objI = 2;
			{
				isHit |= tri1.Intersect(ray, hit);
			}

			if(isHit)
			{
				des = hit.P;//ray._o + ray._dir * hit.t;
				for(int i=0; i<3; i++)
				{
					des[i] = des[i] * 0.5f + 0.5f;
				}
				res.Set(des[0], des[1], des[2]);
				/**/
				//res = red * hit.N.Dot(light.GetDirection(hit.P).GetNormalize());
				res.Clamp();
				image.set(pix, res);
				//printf("hit.normal[%f, %f, %f]\n", hit.N[0], hit.N[1], hit.N[2]);
				//printf("hit.P[%f, %f, %f]\n", hit.P[0], hit.P[1], hit.N[2]);
				//printf("[%i, %i] isHit\n");
			}else
			{
				image.set(pix, black);
			}

		}

	}

	void Unit_test_BVH_Stack()
	{
		bvhStack stack;
		const int listSize = 7;
		int testList[listSize] = {6, 8, 90, 34, 342, 32, 9823};

		printf("Test list:\n");
		for(int i=0; i<listSize; i++)
		{
			printf("%d, ", testList[i]);
			stack.Push(testList[i]);
		}
		printf("\n");

		printf("Stack become:\n");
		for(int i=0; i<stack.GetStackSize(); i++)
		{
			printf("%d, ", stack[i]);
		}
		printf("\n");
		printf("Stack current: %d\n", stack.GetCurrent());

		printf("Poping stack:\n");
		while(!stack.IsEmpty())
		{
			printf("Pop %d.\n", stack.Pop());
			//for(int i=0; i<=stack.GetCurrent(); i++)
			for(int i=0; i<stack.GetStackSize(); i++)
			{
				printf("%d, ", stack[i]);
			}
			printf("current=%d\n", stack.GetCurrent());
		}

		printf("Finish BVH stack unit test.\n");
	}

	void Unit_test_GetMemInfo()
	{
		printf("Testing getting memory information\n");
		int endMem = GetMemoryEndAddr();
		int memsize= GetMemorySize();
		printf("End mem: %d\nMem size: %d\n", endMem, memsize);
	}
    
    void Unit_test_Atomicadd()
    {
        printf("Testing atomicadd(7, 2)\n");
        // register 7 for unit test here
        int val = 0;
        
        for (int i=0; i<10; i++) {
            val = atomicadd(7, 2);
            printf("Val is %d\n", val);
        }
        
        printf("Atomic add function testing done\n");
    }

    void Unit_test_PosToZcurve()
    {
    	printf("Testing pos3d to z-curve mapping\n");
    	int pm_s_addr = GetPhotonMapAddr();
    	const int numVec = 1;
    	storei(numVec, pm_s_addr, 0);
    	Vector3 vec[numVec];
    	Photon phts[numVec];
    	int pht_size = sizeof(Photon) / 4;
    	int ix, iy, iz;
    	ix=iy=iz=0;
    	int prec = 0; // precision
    	int fac = 1;
    	for(int i=0; i<prec; i++)
    	{
    		fac *= 10;
    	}

    	const float facf = static_cast<float>(fac);

    	for(int i=0; i<numVec; i++)
    	{
            //int i=0;
    		/**
    		switch((i-1)%3)
    		{
    			case 0: ++ix; break;
    			case 1: ++iy; break;
    			case 2: ++iz; break;
    			default: break;
    		}

    		vec[i][0] = static_cast<float>(ix) / facf;
    		vec[i][1] = static_cast<float>(iy) / facf;
    		vec[i][2] = static_cast<float>(iz) / facf;
			// **/
    		ConvertZcurveIndex(i, vec[i], prec);

    		phts[i].pRay._o = vec[i];
    		phts[i].flux.Set(i%3, (i+1)%3, (i+2)%3);

    		phts[i].SaveToMemory(pm_s_addr + 1 + i*pht_size);
    	}
#if 0
		int zIndex = 0;
    	for(int i=0; i<numVec; i++)
    	{
    		printf("vec %d: ", i);
    		vec[i].DebugPrint();
    		printf(" ");
    		GetZcurveIndex(vec[i], zIndex, prec);
    		printf("%d\n", zIndex);
    	}
#endif
    	printf("Testing pos3d to z-curve done\n");
    }

} // namespace AtomTrace
